Working with Menus: Checkmarks

© 2004, Brad Moore

author website:

http://www.freewebs.com/lb-connection

Home

Tip Corner

API Corner

Just for fun

Timing Events

QCard Lesson

Menus: Checkmarks

Dissolve Demo

Encryption

Poker Game

Snip Manager

Demos

Newsletter help

Index


This is a brief explanation of how to work with Liberty Basic menus, dynamically setting and resetting checkmarks. There is an accompanying program that shows the concepts in a fun and delicious way. It was created with Tom Nally specifically in mind, and I hope he enjoys it. This article comes as a result of a question asked on the Liberty Basic Conforum's site.

Menus are native to Liberty Basic. In fact in version 4 of the language, you can enable and disable menu items individually. However, placing a checkmark in front of a selected menu item (as is often done in option type menus) is not possible using LB native commands. For this we must use the Windows API.

There are four API calls required. Three of the API calls are used to obtain the handle and menu IDs to the menu items that the check marks will be applied to. We will look at these in depth in a minute. First lets examine the relationship of menu, submenu and window.

Notice in the example above that we have several objects:


In order to apply a checkmark or remove one, you must have the SubMenuItem ID. It is obtained by walking down the path from window, to menu, to submenu and finally to submenuitem. We use a series of API calls to do this:

GetMenu is the first - which returns the handle of the menu object container:


Function GetMenu(hWnd)
    CallDLL #user32, "GetMenu",hWnd As Long,GetMenu As Long
    End Function

Once that is known we can get an individual submenu ID using the GetSubMenu API call. The actual ID is returned by the API call as the result.


Function GetSubMenu(hMenuBar,nPos)
    CallDLL #user32, "GetSubMenu",_
    hMenuBar As Long, nPos As Long, GetSubMenu As Long
    End Function

Using the submenu ID we can get an individual SubMenuItem ID. I like to get all the IDs of any submenuitem I will be setting a check mark for, and storeing them in an array. I will show that code in a moment. The API call GetMenuItemID is used to get this ID:


Function GetMenuItemID(hSubMenu,nPos)
    CallDLL #user32, "GetMenuItemID", hSubMenu As Long, _
    nPos As Long, GetMenuItemID  As Long
    End Function

Above each of the functions are encapsulated in functions which are courtesy of Alyce Watson and the fantastic Liberty Basic Workshop. I implemented these functions in the code of my demo program in the following way:


    '** Get the main window handle
    hWnd = hwnd(#main)

    '** Get the menu handle for the menus
    hMenu = GetMenu(hWnd)

    '** Get the sub menu ID for the Skill Level menu only
    'Menus are numbered from 0 up.  Menu 1 is the second from left.
    hSubMenu = GetSubMenu(hMenu,1)
    
    '** Finally, get the menu ID's for the items on the Skill Level menu
    'there are 10 of them, so I will use an array
    for x = 0 to 9
        menuID = GetMenuItemID(hSubMenu,x)
        subMenuID(x) = menuID
    next x

Once we have the SubMenuItemIDs we can set and reset the checkmark using the API call CheckMenuItem. Alyce actually implemented it in LB Workshop as a function. The API call returns a status indicating success and failure. I really did not care for the purpose of this demo, so I changed the function into a sub. You will probably want to use the function in your code if you are writing programs that will be released into the public, as it is good practice to trap all potential errors and handle them.


sub CheckMenuItem hSubMenu,menuid,wCheck
    'Note: Alyce wrote this as a function, but I changed to a sub
    'as a function it returns the status of the call, but that is
    'not critical to my needs.
    'wCheck 0=unchecked,8=checked
    CallDLL #user32,"CheckMenuItem", hSubMenu As Long,_
    menuid As Long, wCheck As Long, result As Long
    End sub

In my implementation I only wanted one menu item checked at any given time, so I unchecked the current choice and check the new choice. This is not required, you can have any, all or even no menu items checked in your program.

I implemented the check marking component of my code as a function that receives the old (current) menu item index and the new choice. It looks up the actual SubMenuItemID from the array I built elsewhere. Here is the code:


function updatedonut(current, newpick, hSubMenu)
    
    'first I want to uncheck the menu item that is current
    call CheckMenuItem hSubMenu,subMenuID(current),0

    'Next check the menu item that is the new pick
    call CheckMenuItem hSubMenu,subMenuID(newpick),8
    
[snip]
    
    'finally set the function to return the new current
    updatedonut = newpick

end function

The demonstration program is simple, but entertaining. I would recommend not running it when you are hungry, or you will be jumping into your car and heading out to the closest KrispyKreme! You will find the program and its associated image files in an archive that is part of the newsletter download. You will need all the files to get the program to run. I have included the source code here for your viewing pleasure:



'** Created by LB Workshop (Functions/Subs courtesy Alyce Watson)
'** Donut Picker, by Brad Moore - 2004
'** Released into the Public Domain: July 31, 2004

dim subMenuID(10)
dim images$(10)

'load the images
for x = 0 to 10
    read a$
    images$(x) = a$
    a$ = a$ + ".bmp"
    loadbmp images$(x), a$
next x

    NOMAINWIN
    WindowWidth = 200 : WindowHeight = 200
    UpperLeftX = INT((DisplayWidth-WindowWidth)/2)
    UpperLeftY = INT((DisplayHeight-WindowHeight)/2)

'set up the controls and menus for our window
Menu        #main, "File", "About", [about], | , "Exit", [quit]
Menu        #main, "Donuts", "Glazed", [glazed], "Chocolate Glazed", [chocglaze],_
                   "Sugar Coated", [sugar], "Chocolate Sprinkle", [sprinkle], _
                   "Glazed Cruller", [cruller], "Chocolate Cruller", [choccruller], _
                   "Powedered Cake", [powder], "Dulce de Leche", [dulce], _
                   "Caramel Kreme Crunch", [caramel], "NY Cheese Crunch", [nycheese]
graphicbox  #main.gb,-5,-5, 200, 155


Open "Donut Picker" for Window as #main

    'final touches to the window
    #main "trapclose [quit]"
    #main.gb "down; fill White; flush"
    #main "font ms_sans_serif 10"
    #main.gb "up; goto 32 10;down; drawbmp kk; flush"
    
'-------------------------------------------------------------------------------
'   Get the handles of the menus and submenus for management of check menu items

    '** Get the main window handle
    hWnd = hwnd(#main)

    '** Get the menu handle for the menus
    hMenu = GetMenu(hWnd)

    '** Get the sub menu ID for the Skill Level menu only
    'Menus are numbered from 0 up.  Menu 1 is the second from left.
    hSubMenu = GetSubMenu(hMenu,1)
    
    '** Finally, get the menu ID's for the items on the Skill Level menu
    'there are 10 of them, so I will use an array
    for x = 0 to 9
        menuID = GetMenuItemID(hSubMenu,x)
        subMenuID(x) = menuID
    next x

    'The last item to do here is to check the current menu item and display the new graphic
    'remember that menus are numbered from zero
    current = 0
    current = updatedonut(current, 0, hSubMenu)

[loop]
    Wait

[quit]
    close #main : END

[about]
    a$ = "About Donut Picker" + chr$(13) + _
         "Donut Picker was concieved as a demonstration program to show how to" + chr$(13) + _ 
         "create checkmark menu options in Liberty Basic.  This program borrows" + chr$(13) + _
         "elements from many sources.  Many of the functions and API calls are" + chr$(13) + _
         "courtesy of Alyce Watson.  Check out the many resourses at her web-" + chr$(13) + _
         "site: http://alycesrestaurant.com."
    notice a$
    Wait

[glazed]
    current = updatedonut(current, 0, hSubMenu)
    Wait

[chocglaze]
    current = updatedonut(current, 1, hSubMenu)
    Wait

[sugar]
    current = updatedonut(current, 2, hSubMenu)
    Wait

[sprinkle]
    current = updatedonut(current, 3, hSubMenu)
    Wait

[cruller]
    current = updatedonut(current, 4, hSubMenu)
    Wait

[choccruller]
    current = updatedonut(current, 5, hSubMenu)
    Wait

[powder]
    current = updatedonut(current, 6, hSubMenu)
    Wait

[dulce]
    current = updatedonut(current, 7, hSubMenu)
    Wait

[caramel]
    current = updatedonut(current, 8, hSubMenu)
    Wait

[nycheese]
    current = updatedonut(current, 9, hSubMenu)
    Wait
    
    
'-----------------------------------------------------------------------------
' Data

data glazed, chocglaze, sugar, sprinkle, cruller, choccruller
data powder, dulce, caramel, nycheese, kk


'-----------------------------------------------------------------------------
' UpdateDonut sub program does the work of checking menus and
' displaying donut images

function updatedonut(current, newpick, hSubMenu)
    
    'first I want to uncheck the menu item that is current
    call CheckMenuItem hSubMenu,subMenuID(current),0

    'Next check the menu item that is the new pick
    call CheckMenuItem hSubMenu,subMenuID(newpick),8
    
    'Now lets load the new image
    #main.gb "delsegment donuts; color white; backcolor white"
    #main.gb "up; goto 30 70; down; boxfilled 160 160"
    '#main.gb "up; goto 58 70; down; drawbmp ";images$(newpick);" 58 70"
    #main.gb "down; drawbmp ";images$(newpick);" 58 79"
    #main.gb "flush donuts"
    
    'finally set the function to return the new current
    updatedonut = newpick

end function



'-----------------------------------------------------------------------------
'Functions: courtsey of Alyce Watson's LB Workshop API wrapper
'checkout LB Workshop at http://alycesrestaurant.com

Function GetMenuItemID(hSubMenu,nPos)
    CallDLL #user32, "GetMenuItemID", hSubMenu As Long, _
    nPos As Long, GetMenuItemID  As Long
    End Function

Function GetSubMenu(hMenuBar,nPos)
    CallDLL #user32, "GetSubMenu",_
    hMenuBar As Long, nPos As Long, GetSubMenu As Long
    End Function

Function GetMenu(hWnd)
    CallDLL #user32, "GetMenu",hWnd As Long,GetMenu As Long
    End Function

sub CheckMenuItem hSubMenu,menuid,wCheck
    'Note: Alyce wrote this as a function, but I changed to a sub
    'as a function it returns the status of the call, but that is
    'not critical to my needs.
    'wCheck 0=unchecked,8=checked
    CallDLL #user32,"CheckMenuItem", hSubMenu As Long,_
    menuid As Long, wCheck As Long, result As Long
    End sub

by Brad Moore - article copyright 2004. Program source in the public domain.


Home

Tip Corner

API Corner

Just for fun

Timing Events

QCard Lesson

Menus: Checkmarks

Dissolve Demo

Encryption

Poker Game

Snip Manager

Demos

Newsletter help

Index